#include	"compiler.h"
#include	"midiout.h"


#define	RELBIT		6

#define	SAMPMULREL(a, b)		((a) * (b >> RELBIT))
#define	SAMPMULVOL(a, b)		((a) * (b))

#define RESAMPLING(d, s, p) {									\
	int		dat1;												\
	int		dat2;												\
	int		div;												\
	dat1 = (s)[(p) >> FREQ_SHIFT];								\
	div = (p) & FREQ_MASK;										\
	if (div) {													\
		dat2 = (s)[((p) >> FREQ_SHIFT) + 1];					\
		dat1 += ((dat2 - dat1) * div) >> FREQ_SHIFT;			\
	}															\
	*(d) = (_SAMPLE)dat1;										\
}


#if defined(ENABLE_VIRLATE)

// ---- vibrate

static int vibrate_update(VOICE v) {

	int		depth;
	int		phase;
	int		pitch;
	float	step;

	depth = v->sample->vibrate_depth << 6;
	if (v->vibrate.sweepstep) {
		v->vibrate.sweepcount += v->vibrate.sweepstep;
		if (v->vibrate.sweepcount >= (1 << VIBSWEEP_SHIFT)) {
			v->vibrate.sweepstep = 0;
		}
		else {
			depth *= v->vibrate.sweepcount;
			depth >>= VIBSWEEP_SHIFT;
		}
	}

	phase = v->vibrate.phase++;
	phase &= (1 << VIBRATE_SHIFT) - 1;
	pitch = (vibsin12[phase] * depth) >> 12;
	step = v->freqnow * bendhtbl[(pitch >> (6 + 7)) + 24] *
												bendltbl[(pitch >> 7) & 0x3f];
	return((int)(step * (float)(1 << FREQ_SHIFT)));
}

static SAMPLE resample_vibrate(VOICE v, SAMPLE dst, SAMPLE dstterm) {

	SAMPLE	src;
	SAMPLE	dstbreak;
	int		pos;
	int		step;
	int		last;
	int		cnt;

	src = v->sample->data;
	pos = v->samppos;
	step = v->sampstep;
	if (step < 0) {
		step *= -1;
	}
	last = v->sample->datasize;

	cnt = v->vibrate.count;
	if (cnt == 0) {
		cnt = v->vibrate.rate;
		step = vibrate_update(v);
		v->sampstep = step;
	}

	dstbreak = dst + cnt;
	if (dstbreak < dstterm) {
		do {
			do {
				RESAMPLING(dst, src, pos);
				dst++;
				pos += step;
				if (pos > last) {
					voice_setfree(v);
					return(dst);
				}
			} while(dst < dstbreak);

			step = vibrate_update(v);
			cnt = v->vibrate.rate;
			dstbreak += cnt;
		} while(dstbreak < dstterm);
		v->sampstep = step;
	}
	v->vibrate.count = cnt - (dstterm - dst);
	do {
		RESAMPLING(dst, src, pos);
		dst++;
		pos += step;
		if (pos > last) {
			voice_setfree(v);
			return(dst);
		}
	} while(dst < dstterm);
	v->samppos = pos;
	return(dst);
}

static SAMPLE resample_vibloop(VOICE v, SAMPLE dst, SAMPLE dstterm) {

	SAMPLE	src;
	SAMPLE	dstbreak;
	int		pos;
	int		step;
	int		last;
	int		cnt;

	src = v->sample->data;
	pos = v->samppos;
	step = v->sampstep;
	last = v->sample->loopend;

	cnt = v->vibrate.count;
	if (cnt == 0) {
		step = vibrate_update(v);
		cnt = v->vibrate.rate;
		v->sampstep = step;
	}

	dstbreak = dst + cnt;
	if (dstbreak < dstterm) {
		do {
			do {
				RESAMPLING(dst, src, pos);
				dst++;
				pos += step;
				if (pos > last) {
					pos -= last - v->sample->loopstart;
				}
			} while(dst < dstbreak);
			step = vibrate_update(v);
			cnt = v->vibrate.rate;
			dstbreak += cnt;
		} while(dstbreak < dstterm);
		v->sampstep = step;
	}
	v->vibrate.count = cnt - (dstterm - dst);
	do {
		RESAMPLING(dst, src, pos);
		dst++;
		pos += step;
		if (pos > last) {
			pos -= last - v->sample->loopstart;
		}
	} while(dst < dstterm);

	v->samppos = pos;
	return(dst);
}

static SAMPLE resample_vibround(VOICE v, SAMPLE dst, SAMPLE dstterm) {

	SAMPLE	src;
	SAMPLE	dstbreak;
	int		pos;
	int		step;
	int		start;
	int		last;
	int		cnt;
	BOOL	stepsign;

	src = v->sample->data;
	pos = v->samppos;
	step = v->sampstep;
	start = v->sample->loopstart;
	last = v->sample->loopend;

	cnt = v->vibrate.count;
	if (cnt == 0) {
		cnt = v->vibrate.rate;
		stepsign = (step < 0);
		step = vibrate_update(v);
		if (stepsign) {
			step = 0 - step;
		}
		v->sampstep = step;
	}
	dstbreak = dst + cnt;

	if (step < 0) {
		goto rr_backward;
	}

	if (dstbreak < dstterm) {
		do {
			do {
				RESAMPLING(dst, src, pos);
				dst++;
				pos += step;
				if (pos > last) {
					pos = last - (pos - last);
					step *= -1;
					goto rr_backward1;
				}
rr_forward1:;
			} while(dst < dstbreak);
			step = vibrate_update(v);
			cnt = v->vibrate.rate;
			dstbreak += cnt;
		} while(dstbreak < dstterm);
	}
	v->vibrate.count = cnt - (dstterm - dst);
	do {
		RESAMPLING(dst, src, pos);
		dst++;
		pos += step;
		if (pos > last) {
			pos = last - (pos - last);
			step *= -1;
			goto rr_backward2;
		}
rr_forward2:;
	} while(dst < dstterm);
	goto rr_done;

rr_backward:
	if (dstbreak < dstterm) {
		do {
			do {
				RESAMPLING(dst, src, pos);
				dst++;
				pos += step;
				if (pos < start) {
					pos = start + (start - pos);
					step *= -1;
					goto rr_forward1;
				}
rr_backward1:;
			} while(dst < dstbreak);
			step = 0 - vibrate_update(v);
			cnt = v->vibrate.rate;
			dstbreak += cnt;
		} while(dstbreak < dstterm);
	}
	v->vibrate.count = cnt - (dstterm - dst);
	do {
		RESAMPLING(dst, src, pos);
		dst++;
		pos += step;
		if (pos < start) {
			pos = start + (start - pos);
			step *= -1;
			goto rr_forward2;
		}
rr_backward2:;
	} while(dst < dstterm);

rr_done:
	v->samppos = pos;
	v->sampstep = step;
	return(dst);
}

#endif


// ---- normal

static SAMPLE resample_normal(VOICE v, SAMPLE dst, SAMPLE dstterm) {

	SAMPLE	src;
	int		pos;
	int		step;
	int		last;

	src = v->sample->data;
	pos = v->samppos;
	step = v->sampstep;
	if (step < 0) {
		step *= -1;
	}
	last = v->sample->datasize;

	do {
		RESAMPLING(dst, src, pos);
		dst++;
		pos += step;
		if (pos > last) {
			voice_setfree(v);
			return(dst);
		}
	} while(dst < dstterm);

	v->samppos = pos;
	return(dst);
}

static SAMPLE resample_loop(VOICE v, SAMPLE dst, SAMPLE dstterm) {

	SAMPLE	src;
	int		pos;
	int		step;
	int		last;

	src = v->sample->data;
	pos = v->samppos;
	step = v->sampstep;
	last = v->sample->loopend;
	do {
		RESAMPLING(dst, src, pos);
		dst++;
		pos += step;
		if (pos > last) {
			pos -= last - v->sample->loopstart;
		}
	} while(dst < dstterm);
	v->samppos = pos;
	return(dst);
}

static SAMPLE resample_round(VOICE v, SAMPLE dst, SAMPLE dstterm) {

	SAMPLE	src;
	int		pos;
	int		step;
	int		start;
	int		last;

	src = v->sample->data;
	pos = v->samppos;
	step = v->sampstep;
	start = v->sample->loopstart;
	last = v->sample->loopend;

	if (step < 0) {
		goto rr_backward;
	}
rr_forward:
	do {
		RESAMPLING(dst, src, pos);
		dst++;
		pos += step;
		if (pos > last) {
			pos = last - (pos - last);
			step *= -1;
			if (dst < dstterm) {
				goto rr_backward;
			}
		}
	} while(dst < dstterm);
	goto rr_done;

rr_backward:
	do {
		RESAMPLING(dst, src, pos);
		dst++;
		pos += step;
		if (pos < start) {
			pos = start + (start - pos);
			step *= -1;
			if (dst < dstterm) {
				goto rr_forward;
			}
		}
	} while(dst < dstterm);

rr_done:
	v->samppos = pos;
	v->sampstep = step;
	return(dst);
}


// ----

int envlope_setphase(VOICE v, int phase) {

	do {
		if (phase >= 6) {
			voice_setfree(v);
			return(1);
		}
		if (v->sample->mode & MODE_ENVELOPE) {
			if (v->phase & (VOICE_ON | VOICE_SUSTAIN)) {
				if (phase >= 3) {
					v->envstep = 0;
					return(0);
				}
			}
		}
		phase += 1;
	} while(v->envvol == v->sample->envpostbl[phase-1]);
	v->envphase = phase;
	v->envterm = v->sample->envpostbl[phase-1];
	v->envstep = v->sample->envratetbl[phase-1];
	if (v->envterm < v->envvol) {
		v->envstep *= -1;
	}
	return(0);
}

void envelope_updates(VOICE v) {

	int		envl;
	int		envr;
	int		vol;

	envl = v->volleft;
	if ((v->flag & VOICE_MIXMASK) == VOICE_MIXNORMAL) {
		envr = v->volright;
#if defined(ENABLE_TREMOLO)
		if (v->tremolo.step) {
			envl *= v->tremolo.volume;
			envl >>= 12;
			envr *= v->tremolo.volume;
			envr >>= 12;
		}
#endif
		if (v->sample->mode & MODE_ENVELOPE) {
			vol = voltbl12[v->envvol >> (7 + 9 + 1)];
			envl *= vol;
			envl >>= 12;
			envr *= vol;
			envr >>= 12;
		}
		envl >>= (16 - SAMP_SHIFT);
		if (envl > SAMP_LIMIT) {
			envl = SAMP_LIMIT;
		}
		v->envleft = envl;
		envr >>= (16 - SAMP_SHIFT);
		if (envr > SAMP_LIMIT) {
			envr = SAMP_LIMIT;
		}
		v->envright = envr;
	}
	else {
#if defined(ENABLE_TREMOLO)
		if (v->tremolo.step) {
			envl *= v->tremolo.volume;
			envl >>= 12;
		}
#endif
		if (v->sample->mode & MODE_ENVELOPE) {
			envl *= voltbl12[v->envvol >> (7 + 9 + 1)];
			envl >>= 12;
		}
		envl >>= (16 - SAMP_SHIFT);
		if (envl > SAMP_LIMIT) {
			envl = SAMP_LIMIT;
		}
		v->envleft = envl;
	}
}

#if defined(ENABLE_TREMOLO)
static void tremolo_update(VOICE v) {

	int		depth;
	int		cnt;
	int		vol;
	int		pos;

	depth = v->sample->tremolo_depth << 8;
	if (v->tremolo.sweepstep) {
		v->tremolo.sweepcount += v->tremolo.sweepstep;
		if (v->tremolo.sweepcount < (1 << TRESWEEP_SHIFT)) {
			depth *= v->tremolo.sweepcount;
			depth >>= TRESWEEP_SHIFT;
		}
		else {
			v->tremolo.sweepstep = 0;
		}
	}
	v->tremolo.count += v->tremolo.step;
	cnt = v->tremolo.count >> TRERATE_SHIFT;
	pos = cnt & ((1 << (SINENT_BIT - 2)) - 1);
	if (cnt & (1 << (SINENT_BIT - 2))) {
		pos ^= ((1 << (SINENT_BIT - 2)) - 1);
	}
	vol = envsin12q[pos];
	if (cnt & (1 << (SINENT_BIT - 1))) {
		vol = 0 - vol;
	}
	v->tremolo.volume = (1 << 12) - ((vol * depth) >> (2 + 8 + 8));
}
#endif

static int envelope_update(VOICE v) {

	if (v->envstep) {
		v->envvol += v->envstep;
		if (((v->envstep < 0) && (v->envvol <= v->envterm)) ||
			((v->envstep > 0) && (v->envvol >= v->envterm))) {
			v->envvol = v->envterm;
			if (envlope_setphase(v, v->envphase)) {
				return(1);
			}
			if (v->envstep == 0) {
				voice_setmix(v);
			}
		}
	}
#if defined(ENABLE_TREMOLO)
	if (v->tremolo.step) {
		tremolo_update(v);
	}
#endif
	envelope_updates(v);
	return(0);
}


// ---- release

static void mixrel_normal(VOICE v, SINT32 *dst, SAMPLE src, SAMPLE srcterm) {

	int		samples;
	SINT32	voll;
	SINT32	rell;
	SINT32	volr;
	SINT32	relr;
	_SAMPLE	s;

	samples = srcterm - src;
	voll = v->envleft << RELBIT;
	rell = -(voll / samples);
	if (!rell) {
		rell = -1;
	}
	volr = v->envright << RELBIT;
	relr = -(volr / samples);
	if (!relr) {
		relr = -1;
	}
	do {
		s = *src++;
		voll += rell;
		if (voll > 0) {
			dst[0] += SAMPMULREL(s, voll);
		}
		volr += relr;
		if (volr > 0) {
			dst[1] += SAMPMULREL(s, volr);
		}
		dst += 2;
	} while(src < srcterm);
}

static void mixrel_left(VOICE v, SINT32 *dst, SAMPLE src, SAMPLE srcterm) {

	SINT32	vol;
	SINT32	rel;
	_SAMPLE	s;

	vol = v->envleft << RELBIT;
	rel = - (vol / (srcterm - src));
	if (!rel) {
		rel = -1;
	}
	do {
		vol += rel;
		if (vol <= 0) {
			break;
		}
		s = *src++;
		dst[0] += SAMPMULREL(s, vol);
		dst += 2;
	} while(src < srcterm);
}

static void mixrel_right(VOICE v, SINT32 *dst, SAMPLE src, SAMPLE srcterm) {

	mixrel_left(v, dst + 1, src, srcterm);
}

static void mixrel_centre(VOICE v, SINT32 *dst, SAMPLE src, SAMPLE srcterm) {

	SINT32	vol;
	SINT32	rel;
	_SAMPLE	s;
	SINT32	d;

	vol = v->envleft << RELBIT;
	rel = - (vol / (srcterm - src));
	if (!rel) {
		rel = -1;
	}
	do {
		vol += rel;
		if (vol <= 0) {
			break;
		}
		s = *src++;
		d = SAMPMULREL(s, vol);
		dst[0] += d;
		dst[1] += d;
		dst += 2;
	} while(src < srcterm);
}


// ---- normal

static void mixnor_normal(VOICE v, SINT32 *dst, SAMPLE src, SAMPLE srcterm) {

	SINT32	voll;
	SINT32	volr;
	_SAMPLE	s;

	voll = v->envleft;
	volr = v->envright;
	do {
		s = *src++;
		dst[0] += SAMPMULVOL(s, voll);
		dst[1] += SAMPMULVOL(s, volr);
		dst += 2;
	} while(src < srcterm);
}

static void mixnor_left(VOICE v, SINT32 *dst, SAMPLE src, SAMPLE srcterm) {

	SINT32	vol;
	_SAMPLE	s;

	vol = v->envleft;
	do {
		s = *src++;
		dst[0] += SAMPMULVOL(s, vol);
		dst += 2;
	} while(src < srcterm);
}

static void mixnor_right(VOICE v, SINT32 *dst, SAMPLE src, SAMPLE srcterm) {

	SINT32	vol;
	_SAMPLE	s;

	vol = v->envleft;
	do {
		s = *src++;
		dst[1] += SAMPMULVOL(s, vol);
		dst += 2;
	} while(src < srcterm);
}

static void mixnor_centre(VOICE v, SINT32 *dst, SAMPLE src, SAMPLE srcterm) {

	SINT32	vol;
	_SAMPLE	s;
	SINT32	d;

	vol = v->envleft;
	do {
		s = *src++;
		d = SAMPMULVOL(s, vol);
		dst[0] += d;
		dst[1] += d;
		dst += 2;
	} while(src < srcterm);
}


// ---- env

static void mixenv_normal(VOICE v, SINT32 *dst, SAMPLE src, SAMPLE srcterm) {

	int		cnt;
	SINT32	voll;
	SINT32	volr;
	SAMPLE	srcbreak;
	_SAMPLE	s;

	cnt = v->envcount;
	if (!cnt) {
		cnt = ENV_RATE;
		if (envelope_update(v)) {
			return;
		}
	}
	voll = v->envleft;
	volr = v->envright;

	srcbreak = src + cnt;
	if (srcbreak < srcterm) {
		do {
			do {
				s = *src++;
				dst[0] += SAMPMULVOL(s, voll);
				dst[1] += SAMPMULVOL(s, volr);
				dst += 2;
			} while(src < srcbreak);
			cnt = ENV_RATE;
			if (envelope_update(v)) {
				return;
			}
			voll = v->envleft;
			volr = v->envright;
			srcbreak = src + cnt;
		} while(srcbreak < srcterm);
	}

	v->envcount = cnt - (srcterm - src);
	do {
		s = *src++;
		dst[0] += SAMPMULVOL(s, voll);
		dst[1] += SAMPMULVOL(s, volr);
		dst += 2;
	} while(src < srcterm);
}

static void mixenv_left(VOICE v, SINT32 *dst, SAMPLE src, SAMPLE srcterm) {

	int		cnt;
	SINT32	vol;
	SAMPLE	srcbreak;
	_SAMPLE	s;

	cnt = v->envcount;
	if (!cnt) {
		cnt = ENV_RATE;
		if (envelope_update(v)) {
			return;
		}
	}
	vol = v->envleft;

	srcbreak = src + cnt;
	if (srcbreak < srcterm) {
		do {
			do {
				s = *src++;
				dst[0] += SAMPMULVOL(s, vol);
				dst += 2;
			} while(src < srcbreak);
			cnt = ENV_RATE;
			if (envelope_update(v)) {
				return;
			}
			vol = v->envleft;
			srcbreak = src + cnt;
		} while(srcbreak < srcterm);
	}

	v->envcount = cnt - (srcterm - src);
	do {
		s = *src++;
		dst[0] += SAMPMULVOL(s, vol);
		dst += 2;
	} while(src < srcterm);
}

static void mixenv_right(VOICE v, SINT32 *dst, SAMPLE src, SAMPLE srcterm) {

	mixenv_left(v, dst + 1, src, srcterm);
}

static void mixenv_centre(VOICE v, SINT32 *dst, SAMPLE src, SAMPLE srcterm) {

	int		cnt;
	SINT32	vol;
	SAMPLE	srcbreak;
	_SAMPLE	s;
	SINT32	d;

	cnt = v->envcount;
	if (!cnt) {
		cnt = ENV_RATE;
		if (envelope_update(v)) {
			return;
		}
	}
	vol = v->envleft;

	srcbreak = src + cnt;
	if (srcbreak < srcterm) {
		do {
			do {
				s = *src++;
				d = SAMPMULVOL(s, vol);
				dst[0] += d;
				dst[1] += d;
				dst += 2;
			} while(src < srcbreak);
			cnt = ENV_RATE;
			if (envelope_update(v)) {
				return;
			}
			vol = v->envleft;
			srcbreak = src + cnt;
		} while(srcbreak < srcterm);
	}

	v->envcount = cnt - (srcterm - src);
	do {
		s = *src++;
		d = SAMPMULVOL(s, vol);
		dst[0] += d;
		dst[1] += d;
		dst += 2;
	} while(src < srcterm);
}


// ----

static const RESPROC resproc[] = {
		resample_normal,	resample_loop,		resample_round,
#if defined(ENABLE_VIRLATE)
		resample_vibrate,	resample_vibloop,	resample_vibround,
#endif
};

void voice_setphase(VOICE v, BYTE phase) {

	int		proc;
	int		mode;

	v->phase = phase;

#if defined(ENABLE_VIRLATE)
	proc = (v->vibrate.rate)?3:0;
#else
	proc = 0;
#endif
	mode = v->sample->mode;
	if ((mode & MODE_LOOPING) &&
		((mode & MODE_ENVELOPE) || (phase & (VOICE_ON | VOICE_SUSTAIN)))) {
		proc++;
		if (mode & MODE_PINGPONG) {
			proc++;
		}
	}
	v->resamp = resproc[proc];
}


// ----

static const MIXPROC mixproc[] = {
			mixnor_normal,	mixnor_left,	mixnor_right,	mixnor_centre,
			mixenv_normal,	mixenv_left,	mixenv_right,	mixenv_centre,
			mixrel_normal,	mixrel_left,	mixrel_right,	mixrel_centre};

void voice_setmix(VOICE v) {

	int		proc;

	proc = v->flag & VOICE_MIXMASK;
	if (!(v->phase & VOICE_REL)) {
#if defined(ENABLE_TREMOLO)
		if ((v->envstep != 0) || (v->tremolo.step != 0))
#else
		if (v->envstep != 0)
#endif
		{
			proc += 1 << 2;
		}
	}
	else {
		proc += 2 << 2;
	}
	v->mix = mixproc[proc];
}

